<?php

namespace App\Controllers;

use App\Models\MenuModel;
use CodeIgniter\Controller;

define('IS_INSTALL', true);

class Install extends Controller
{

    public $db;
    private $lock;
    protected $menuModel;

    public function __construct()
    {
        helper(['ciams', 'cookie', 'security', 'filesystem']);
        $this->renderer = \Config\Services::renderer();
        $this->db = \Config\Database::connect('default'); //数据库设置
        $this->lock = WRITEPATH . 'install.locks';
        is_file($this->lock) && exit('您已经安装过了，如需重新安装，请删除cache/install.lock文件');
        if (version_compare(PHP_VERSION, '7.2.0') < 0) {
            exit('当前环境PHP' . PHP_VERSION . '，其版本太低，必须是在PHP7.2以上');
        }
    }

    public function index()
    {
        $step = max(1, (int) $this->request->getGet('step'));
        switch ($step) {

            case 1:
                break;

            case 2:

                $check_pass = true;

                $writeAble = $this->_checkFileRight();
                $lowestEnvironment = $this->_getLowestEnvironment();
                $currentEnvironment = $this->_getCurrentEnvironment();
                $recommendEnvironment = $this->_getRecommendEnvironment();

                foreach ($currentEnvironment as $key => $value) {
                    if (false !== strpos($key, '_ischeck') && false === $value) {
                        $check_pass = false;
                    }
                }
                foreach ($writeAble as $value) {
                    if (false === $value) {
                        $check_pass = false;
                    }
                }

                $this->renderer->setData([
                    'writeAble' => $writeAble,
                    'check_pass' => $check_pass,
                    'lowestEnvironment' => $lowestEnvironment,
                    'currentEnvironment' => $currentEnvironment,
                    'recommendEnvironment' => $recommendEnvironment,
                ]);
                break;
            case 3:

                if ($_POST) {
                    $data = $this->request->getPost('data');

                    if (!preg_match('/^[\x7f-\xff\dA-Za-z\.\_]+$/', $data['admin'])) {
                        exit(ams_json(1, '管理员账号格式不正确'));
                    }

                    if (!$data['email'] || !filter_var($data['email'], FILTER_VALIDATE_EMAIL)) {
                        exit(ams_json(1, 'Email格式不正确'));
                    }

                    $salt = ams_salt();
                    $password = md5(sha1($data['password']) . $salt . md5($data['password']));
                    $regip = $this->request->getIPAddress();
                    $uid = ams_create_uid(8);

                    // 导入表结构
                    $dbprefix = $this->db->getPrefix();
                    $this->_query(str_replace(
                        ['{dbprefix}', '{uid}', '{username}', '{password}', '{salt}', '{email}', '{regip}', '{regtime}'],
                        [$dbprefix, $uid, $data['admin'], $password, $salt, $data['email'], $regip, time()],
                        file_get_contents(WRITEPATH . 'install/install.sql')
                    ));

                    //导入项目表结构
                    if (is_file(WRITEPATH . 'install/default.sql')) {
                        $this->_query(str_replace(
                            ['{dbprefix}', '{site_url}'],
                            [$dbprefix, strtolower($_SERVER['HTTP_HOST'])],
                            file_get_contents(WRITEPATH . 'install/default.sql')
                        ));
                    }

                    //导入地区表结构
                    if (isset($data['region']) && $data['region'] && is_file(WRITEPATH . 'install/region.sql')) {
                        $this->_query(str_replace('{dbprefix}', $dbprefix, file_get_contents(WRITEPATH . 'install/region.sql')));
                    }

                    //初始化后台菜单
                    $this->menuModel = new MenuModel();
                    $this->menuModel->init();

                    //权限规则
                    $rbac = new \App\Models\RbacModel();
                    $rbac->iniRule();

                    exit(ams_json(0, 'ok', url('install/index', ['step' => $step + 1])));
                }
                break;

            case 4:
                $log = [];
                $sql = file_get_contents(WRITEPATH . 'install/install.sql');
                preg_match_all('/`\{dbprefix\}(.+)`/U', $sql, $match);
                if ($match) {
                    $log = array_unique($match[1]);
                }
                $this->renderer->setData([
                    'log' => implode('<ciams>', $log),
                ]);
                break;

            case 5:
                file_put_contents(WRITEPATH . 'install.locks', time());
                break;
        }

        $vdata = ['step' => $step];
        return view('step_' . $step . '.html', $vdata);
    }

    // 执行sql
    private function _query($sql)
    {
        if (!$sql) {
            return null;
        }
        $sql_data = explode(';SQL_CIAMS_EOL', trim(str_replace([PHP_EOL, chr(13), chr(10)], 'SQL_CIAMS_EOL', $sql)));
        foreach ($sql_data as $query) {
            if (!$query) {
                continue;
            }
            $ret = '';
            $queries = explode('SQL_CIAMS_EOL', trim($query));
            foreach ($queries as $query) {
                if ((isset($query[0]) && isset($query[1]) && $query[0] . $query[1] == '--') || (isset($query[0]) && $query[0] == '#')) {
                    continue;
                }
                $ret .= $query;
            }
            if (!$ret) {
                continue;
            }
            $this->db->query($ret);
        }
    }

    /**
     * 获得当前的环境信息
     *
     * @return array
     */
    private function _getCurrentEnvironment()
    {
        $lowestEnvironment = $this->_getLowestEnvironment();
        $space = floor(@disk_free_space(WEBPATH) / (1024 * 1024));
        $space = $space ? $space . 'M' : 'unknow';
        $currentUpload = ini_get('file_uploads') ? ini_get('upload_max_filesize') : 'unknow';
        $upload_ischeck = intval($currentUpload) >= intval($lowestEnvironment['upload']) ? true : false;
        $space_ischeck = intval($space) >= intval($lowestEnvironment['space']) ? true : false;
        $version_ischeck = version_compare(phpversion(), $lowestEnvironment['version']) < 0 ? false : true;
        $pdo_mysql_ischeck = extension_loaded('pdo_mysql');
        if (function_exists('mysql_get_client_info')) {
            $mysql = mysql_get_client_info();
            $mysql_ischeck = true;
        } elseif (function_exists('mysqli_get_client_info')) {
            $mysql = mysqli_get_client_info();
            $mysql_ischeck = true;
        } elseif ($pdo_mysql_ischeck) {
            $mysql_ischeck = true;
            $mysql = 'unknow';
        } else {
            $mysql_ischeck = false;
            $mysql = 'unknow';
        }
        if (function_exists('gd_info')) {
            $gdinfo = gd_info();
            $gd = $gdinfo['GD Version'];
            $gd_ischeck = version_compare($lowestEnvironment['gd'], $gd) < 0 ? false : true;
        } else {
            $gd_ischeck = false;
            $gd = 'unknow';
        }
        return [
            'gd' => $gd,
            'os' => PHP_OS,
            'json' => function_exists('json_encode'),
            'space' => $space,
            'mysql' => $mysql,
            'upload' => $currentUpload,
            'version' => phpversion(),
            'pdo_mysql' => $pdo_mysql_ischeck,
            'gd_ischeck' => $gd_ischeck,
            'os_ischeck' => true,
            'space_ischeck' => $space_ischeck,
            'mysql_ischeck' => $mysql_ischeck,
            'version_ischeck' => $version_ischeck,
            'upload_ischeck' => $upload_ischeck,
            'pdo_mysql_ischeck' => $pdo_mysql_ischeck,
        ];
    }

    /**
     * 获取推荐的环境配置信息
     *
     * @return array
     */
    private function _getRecommendEnvironment()
    {
        return [
            'os' => 'Linux',
            'gd' => '>2.0.28',
            'json' => '支持',
            'mysql' => '>5.x.x',
            'space' => '>50M',
            'upload' => '>2M',
            'version' => '>5.3.x',
            'pdo_mysql' => '支持',
        ];
    }

    /**
     * 获取环境的最低配置要求
     *
     * @return array
     */
    private function _getLowestEnvironment()
    {
        return [
            'os' => '不限制',
            'gd' => '2.0',
            'json' => '必须支持',
            'space' => '50M',
            'mysql' => '4.2',
            'upload' => '不限制',
            'version' => '5.3.7',
            'pdo_mysql' => '不限制',
        ];
    }

    /**
     * 检查目录权限
     *
     * @return array
     */
    private function _checkFileRight()
    {

        $files_writeble[] = WEBPATH . 'writable/cache/';
        $files_writeble[] = WEBPATH . 'writable/debugbar/';
        $files_writeble[] = WEBPATH . 'writable/install/';
        $files_writeble[] = WEBPATH . 'writable/logs/';
        $files_writeble[] = WEBPATH . 'writable/module/';
        $files_writeble[] = WEBPATH . 'writable/session/';
        $files_writeble[] = WEBPATH . 'writable/uploads/';

        $files_writeble = array_unique($files_writeble);
        sort($files_writeble);
        $writable = [];

        foreach ($files_writeble as $file) {
            $key = str_replace(WEBPATH, '', $file);
            $isWritable = $this->_checkWriteAble($file) ? true : false;
            if ($isWritable) {
                $flag = false;
                foreach ($writable as $k => $v) {
                    if (0 === strpos($key, $k)) {
                        $flag = true;
                    }
                }
                $flag || $writable[$key] = $isWritable;
            } else {
                $writable[$key] = $isWritable;
            }
        }
        return $writable;
    }

    /**
     * 检查目录可写
     *
     * @param string $pathfile
     * @return boolean
     */
    private function _checkWriteAble($pathfile)
    {
        if (!$pathfile) {
            return false;
        }
        $isDir = in_array(substr($pathfile, -1), ['/', '\\']) ? true : false;
        if ($isDir) {
            if (is_dir($pathfile)) {
                mt_srand((double) microtime() * 1000000);
                $pathfile = $pathfile . 'ams_' . uniqid(mt_rand()) . '.tmp';
            } elseif (@mkdir($pathfile)) {
                return self::_checkWriteAble($pathfile);
            } else {
                return false;
            }
        }
        @chmod($pathfile, 0777);
        $fp = @fopen($pathfile, 'ab');
        if ($fp === false) {
            return false;
        }
        fclose($fp);
        $isDir && @unlink($pathfile);
        return true;
    }
}
